﻿//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007-2011
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
//
// Class StringPrinter.cs
// 
// Class to output the vertex source of a string as a run of glyphs.
//----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;

using AGG;
using AGG.Transform;
using AGG.VertexSource;

namespace AGG.Font
{
    public class StringPrinter : IVertexSource
    {
		int currentChar;
        Vector2D currentOffset;
        IVertexSource currentGlyph;
		
		StyledTypeFace typeFaceStyle;

        String text = "";
		
		public String Text
		{
			get
			{
				return text;
			}
			
			set
			{
				text = value;
			}
		}
		
		public StyledTypeFace TypeFaceStyle
		{
			get
			{
				return typeFaceStyle;
			}
			
			set
			{
				typeFaceStyle = value;
			}
		}

        public StringPrinter(StyledTypeFace typeFaceStyle, String text)
        {
            this.typeFaceStyle = typeFaceStyle;
            this.text = text;
        }
		
        public StringPrinter(StyledTypeFace typeFaceStyle)
        {
            this.typeFaceStyle = typeFaceStyle;
        }
		
		public rect_d Bounds
		{
			get
			{
                Vector2D size = GetSize();;
                return new rect_d(0, TypeFaceStyle.DescentInPixels, size.x, TypeFaceStyle.AscentInPixels);
			}
		}

        public void rewind(int pathId)
        {
            currentChar = 0;
            currentOffset = new Vector2D(0, 0);
            if (text != null && text.Length > 0)
            {
                currentGlyph = typeFaceStyle.GetGlyphForCharacter(text[currentChar]);
                currentGlyph.rewind(0);
            }
        }

        public Path.FlagsAndCommand vertex(out double x, out double y)
        {
            if (Text != null && Text.Length > 0)
            {
                Path.FlagsAndCommand curCommand = currentGlyph.vertex(out x, out y);

                while (curCommand == Path.FlagsAndCommand.CommandStop
                    && currentChar < text.Length - 1)
                {
                    if (currentChar < text.Length)
                    {
                        // pass the next char so the typeFaceStyle do kerning if it needs to.
                        currentOffset.x += typeFaceStyle.GetAdvanceForCharacter(text[currentChar], text[currentChar + 1]);
                    }
                    else
                    {
                        currentOffset.x += typeFaceStyle.GetAdvanceForCharacter(text[currentChar]);
                    }

                    currentChar++;
                    currentGlyph = typeFaceStyle.GetGlyphForCharacter(text[currentChar]);
                    currentGlyph.rewind(0);
                    curCommand = currentGlyph.vertex(out x, out y);
                }

                x += currentOffset.x;
                y += currentOffset.y;

                return curCommand;
            }

            x = 0;
            y = 0;
            return Path.FlagsAndCommand.CommandStop;
        }

        public Vector2D GetSize()
        {
            Vector2D offset;
            GetSize(0, Text.Length - 1, out offset);
            return offset;
        }

        public void GetSize(int characterToMeasureStartIndexInclusive, int characterToMeasureEndIndexInclusive, out Vector2D offset)
        {
        	offset.x = 0;
        	offset.y = TypeFaceStyle.EmSizeInPixels;
   
        	for (int i = characterToMeasureStartIndexInclusive; i < characterToMeasureEndIndexInclusive; i++)
			{
        		offset.x += TypeFaceStyle.GetAdvanceForCharacter (Text[i], Text[i + 1]);
        	}
   
			if(Text.Length > characterToMeasureEndIndexInclusive)
			{
				offset.x += TypeFaceStyle.GetAdvanceForCharacter (Text[characterToMeasureEndIndexInclusive]);
			}
        }

        // this will return the position to the left of the requested character.
        public Vector2D GetOffsetLeftOfCharacterIndex(int characterIndex)
        {
            Vector2D offset = new Vector2D(0, 0);
            if (characterIndex > 0)
            {
                GetSize(0, characterIndex - 1, out offset);
            }
            return offset;
        }

        // If the Text is "TEXT" and the position is less than half the distance to the center
        // of "T" the return value will be 0 if it is between the center of 'T' and the center of 'E'
        // it will be 1 and so on.
        public int GetCharacterIndexToStartBefore(Vector2D position)
        {
            int clostestIndex = -1;
            double clostestDist = double.MaxValue;
            Vector2D offset;
            offset.x = 0;
            offset.y = 0;
            int characterToMeasureStartIndexInclusive = 0;
            int characterToMeasureEndIndexInclusive = Text.Length - 1;
            if (Text.Length > 0)
            {
                characterToMeasureStartIndexInclusive = Math.Max(0, Math.Min(characterToMeasureStartIndexInclusive, Text.Length - 1));
                characterToMeasureEndIndexInclusive = Math.Max(0, Math.Min(characterToMeasureEndIndexInclusive, Text.Length - 1));
                for (int i = characterToMeasureStartIndexInclusive; i <= characterToMeasureEndIndexInclusive; i++)
                {
                    Vector2D delta = offset - position;
                    double distToChar = delta.GetLength();
                    if (distToChar < clostestDist)
                    {
                        clostestDist = distToChar;
                        clostestIndex = i;
                    }

                    Vector2D nextSize;
                    GetSize(i, i, out nextSize);

                    offset += nextSize;
                }

                Vector2D lastDelta = offset - position;
                double lastDistToChar = lastDelta.GetLength();
                if (lastDistToChar < clostestDist)
                {
                    clostestDist = lastDistToChar;
                    // we need to start after the lats character, or before the character after the last.
                    clostestIndex = characterToMeasureEndIndexInclusive + 1;
                }
            }

            return clostestIndex;
        }
	}
}
